﻿using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
using Swashbuckle.AspNetCore.SwaggerUI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Headers;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using static RxxAdmin.Model.CustomApiVersion;
using Serilog;
using RxxAdmin.Common;
using Microsoft.AspNetCore.Authorization;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace RxxAdmin.Extensions;

public static class SwaggerSetup
{
    /// <summary>
    /// 配置Swagger服务
    /// </summary>
    /// <param name="services"></param>
    /// <exception cref="ArgumentNullException"></exception>
    public static void AddSwaggerSetup(this IServiceCollection services)
    {
        if (services == null) throw new ArgumentNullException(nameof(services));

        var basePath = AppContext.BaseDirectory;
        var ApiName = AppSettings.ApiName;
        services.AddSwaggerGen(c =>
        {
            //遍历出全部的版本，做文档信息展示
            typeof(ApiVersions).GetEnumNames().ToList().ForEach(version =>
            {
                c.SwaggerDoc(version, new OpenApiInfo
                {
                    Version = version,
                    Title = $"{ApiName}接口文档——{RuntimeInformation.FrameworkDescription}",
                    //Description = $"{ApiName} HTTP API " + version,
                    //Contact = new OpenApiContact { Name = ApiName, Email = "Blog.Core@xxx.com", Url = new Uri("https://neters.club") },
                    //License = new OpenApiLicense { Name = ApiName + " 官方文档", Url = new Uri("http://apk.neters.club/.doc/") }
                });
                c.OrderActionsBy(o => o.RelativePath);
            });
            c.UseInlineDefinitionsForEnums();
            //显示注释内容
            try
            {
                //方法一：
                ////这个就是刚刚配置的xml文件名
                //var xmlPath = Path.Combine(basePath, "RxxAdmin.Api.xml");
                ////默认的第二个参数是false，这个是controller的注释，记得修改
                //c.IncludeXmlComments(xmlPath, true);

                //方法二：
                // 获取根目录下，所有 xml 完整路径（注：并不会获取二级目录下的文件）
                var directoryInfo = new DirectoryInfo(basePath);
                List<string> xmls = directoryInfo
                     .GetFiles()
                     .Where(f => f.Name.ToLower().EndsWith(".xml"))
                     .Select(f => f.FullName)
                     .ToList();
                foreach (var xml in xmls)
                {
                    c.IncludeXmlComments(xml, true);
                }
            }
            catch (Exception ex)
            {
                Log.Error("xml丢失，请检查并拷贝。\n" + ex.Message);
            }

            //在 Swagger UI 中显示相应的安全要求信息（api有"Authorize" 特性，加上小锁）
            c.OperationFilter<SecurityRequirementsOperationFilter>();
            // api有"Authorize" 特性， header中添加 token 传递到后台
            c.OperationFilter<AddResponseHeadersFilter>();
            c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();

            #region JWT认证Swagger授权
            c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
            {
                Description = "JWT授权(数据将在请求头header中进行传输) 直接在下框中输入token",
                Name = "Authorization",
                In = ParameterLocation.Header,
                //Type = SecuritySchemeType.ApiKey,
                Type = SecuritySchemeType.Http, // 不用加Bearer {token}（中间是空格）
                BearerFormat = "JWT",
                Scheme = "Bearer"
            });
            #endregion
        });
        services.AddSwaggerGenNewtonsoftSupport();
    }
    /// <summary>
    /// 封装Swagge展示
    /// </summary>
    /// <param name="app"></param>
    /// <param name="streamHtml"></param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <exception cref="Exception"></exception>
    public static void UseSwaggerMiddle(this IApplicationBuilder app, Func<Stream> streamHtml)
    {
        if (app == null) throw new ArgumentNullException(nameof(app));
        //SwaggerUI 安全验证
        //app.UseMiddleware<SwaggerBasicAuthMiddleware>();
        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            //根据版本名称倒序 遍历展示
            var apiName = AppSettings.ApiName;
            typeof(ApiVersions).GetEnumNames().OrderByDescending(e => e).ToList().ForEach(version => { 
                c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"RxxAdmin {version}");
            });

            c.SwaggerEndpoint($"https://petstore.swagger.io/v2/swagger.json", $"{apiName} pet");

            // 将swagger首页，设置成我们自定义的页面，记得这个字符串的写法：{项目名.index.html}
            if (streamHtml.Invoke() == null)
            {
                var msg = "index.html的属性，必须设置为嵌入的资源";
                throw new Exception(msg);
            }
            c.IndexStream = streamHtml;

            c.DocExpansion(DocExpansion.None); //->修改界面打开时自动折叠
            // 路径配置，设置为空，表示直接在根域名（localhost:8001）访问该文件,注意localhost:8001/swagger是访问不到的，去launchSettings.json把launchUrl去掉，如果你想换一个路径，直接写名字即可，比如直接写c.RoutePrefix = "doc";
            c.RoutePrefix = "ruxingxing_qingbaomi";
            c.DisplayRequestDuration();
            c.EnablePersistAuthorization();

            //c.HeadContent = $@"<script async='async' id='mini-profiler' src='/profiler/includes.min.js?v=4.5.4+b99c53b4e4'
            //                data-version='4.5.4+b99c53b4e4' data-path='/profiler/'
            //                data-current-id='3d5d1577-a70e-461c-9958-d18e58cf2cd8' data-ids='3d5d1577-a70e-461c-9958-d18e58cf2cd8'
            //                data-position='Left' data-scheme='Light' data-decimal-places='2' data-authorized='true'
            //                data-max-traces='15' data-toggle-shortcut='Alt+P' data-trivial-milliseconds='2.0'
            //                data-ignored-duplicate-execute-types='Open,OpenAsync,Close,CloseAsync'></script>";
        });
    }

    public class SwaggerBasicAuthMiddleware
    {
        private readonly RequestDelegate next;
        public SwaggerBasicAuthMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
        public async Task InvokeAsync(HttpContext context)
        {
            if (context.Request.Path.StartsWithSegments("/ruxingxing_qingbaomi"))
            {
                string authHeader = context.Request.Headers["Authorization"];
                if (authHeader != null && authHeader.StartsWith("Basic "))
                {
                    // Get the credentials from request header
                    var header = AuthenticationHeaderValue.Parse(authHeader);
                    var inBytes = Convert.FromBase64String(header.Parameter);
                    var credentials = Encoding.UTF8.GetString(inBytes).Split(':');
                    var username = credentials[0];
                    var password = credentials[1];
                    // validate credentials
                    if (username.Equals("ruxingxing") && password.Equals("ruxingxing111222###"))
                    {
                        await next.Invoke(context).ConfigureAwait(false);
                        return;
                    }
                }
                context.Response.Headers["WWW-Authenticate"] = "Basic";
                context.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            }
            else
            {
                await next.Invoke(context).ConfigureAwait(false);
            }
        }
    }
}